#include <pthread.h>
#include <stdlib.h>

struct job {
    struct job *j_next;
    struct job *j_prev;
    pthread_t j_id;
};

struct queue{
    struct job *q_head;
    struct job *q_tail;
    pthread_rwlock_t q_lock;
};

/*
    initialize a queue
*/
int queue_init(struct queue *qp){
    if(qp == NULL){
        return -1;
    }
    int err;
    qp->q_head = NULL;
    qp->q_tail = NULL;
    err = pthread_rwlock_init(&qp->q_lock, NULL);
    if(err){
        return err;
    }
    return 0;
}

/*
    insert a job at the head of the queue
*/
void job_insert(struct queue *qp, struct job *jp){
    if(qp == NULL || jp == NULL){
        return -1;
    }
    pthread_rwlock_wrlock(&qp->q_lock);
    jp->j_next = qp->q_head;
    jp->j_prev = NULL;
    if(qp->q_head != NULL){
        qp->q_head->j_prev = jp;
    }else{
        qp->q_tail = jp;
    }
    qp->q_head = jp;
    pthread_rwlock_unlock(&qp->q_lock);
}

/*
    append a job on the tail of the queue
*/
void job_append(struct queue *qp, struct job *jp){
    if(qp == NULL || jp == NULL){
        return -1;
    }
    pthread_rwlock_wrlock(&qp->q_lock);
    jp->j_prev = qp->q_tail;
    jp->j_next = NULL;
    if(qp->q_tail != NULL){
        qp->q_tail->j_next = jp;
    }else{
        qp->q_head = jp;
    }
    qp->q_tail = jp;
    pthread_rwlock_unlock(&qp->q_lock);
}

/*
    remove the given job from a queue
*/
void job_remove(struct queue *qp, struct job *jp){
    if(qp == NULL || jp == NULL){
        return -1;
    }
    pthread_rwlock_wrlock(&qp->q_lock);
    if(jp == qp->q_head){
        qp->q_head = jp->j_next;
        if(qp->q_tail == jp){
            qp->q_tail = NULL;
        }else{
            qp->q_tail->j_next = jp->j_next;
        }
    }else if(jp == qp->q_tail){
        qp->q_tail = jp->j_prev;
        jp->j_prev->j_next = jp->j_next;
    }else{
        jp->j_prev->j_next = jp->j_next;
        jp->j_next->j_prev = jp->j_prev;
    }
}

/*
    find a job for the given threadID
*/

struct job *job_find(struct queue *qp, pthread_t id){
    if(qp == NULL){
        return -1;
    }
    struct job *jp;
    if(pthread_rwlock_rdlock(&qp->q_lock) != 0){
        return NULL;
    }

    for(jp = qp->q_head; jp->j_next != NULL; jp = jp->j_next){
        if(pthread_equal(jp->j_id, id)){
            break;
        }
    }
    pthread_rwlock_unlock(&qp->q_lock);
    return jp;

}